﻿


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using Ext.Net;
using KamLib;


namespace Ext.Net.UIS
{

    /// <summary>
    /// Описывает дерево полей с дополнительными натройками уже для конкретных FieldBox.
    /// </summary>
    public partial class TermNode : IEnumerable<TermNode>
    {

        #region Types

        public class DebugClass
        {
            private readonly TermNode Node;
            public DebugClass(TermNode node) { Node = node; }

            public DebugClass[] Items { get { return Node.Items.Select(a => a.__Debug).ToArray(); } }
            public TermTitle title { get { return Node.Title; } }
            public override string ToString() { return Node.ToString(); }

        }

        public DebugClass __Debug { get { return new DebugClass(this); } }

        #endregion

        #region Constructors

        public TermNode(Context context, TermNode parent, Term term)
        {
            Context = context;
            Term = term;

            if (term != null)
            {
                Title = term.GetTitle(parent);
                LoadFromTerm(term);
            }

            if (parent != null)
            {
                parent.Add(this);

                if (term != null && term.Items.Count == 0)
                    parent.Using(term);
            }
        }

        public TermNode(Context context, TermNode parent, string name)
            : this(context, parent, Term.Get(name, parent))
        {
        }

        #endregion

        private string _id;
        public string ID { get { return _id ?? (_id = Context != null ? Context.NewControlID() : null); } }
        public readonly Context Context;
        public TermNode Parent;
        public Term Term { get; protected set; }
        public TermEditor Editor { get; set; }
        

        #region Store

        public string StoreID { get; set; }
        public bool StoreIsNative { get; protected set; }

        public Store Store
        {
            get
            {
                if (_Store == null)
                {
                    if (Term != null && Term.IsEntity())
                    {
                        _Store = new Store(Context, this);
                        StoreID = _Store.ID;
                        StoreIsNative = true;
                    }
                    else if (Parent != null)
                    {
                        _Store = Parent.Store;
                    }
                }
                return _Store;
            }
            set { _Store = value; }
        } Store _Store;

        /// <summary>
        /// Указать, что данное поле используется внутри этого TermNode
        /// </summary>
        /// <param name="term"></param>
        public void Using(Term term)
        {
            if (StoreIsNative)
                Store.Using(term);
            else if (Parent != null)
                Parent.Using(term);
        }

        #endregion


        #region Control Properties

        public TermTitle Title { get; set; }
        public int Flex { get; set; }
        public int LabelWidth { get; set; }
        public Unit Width { get; set; }

        private int _length;
        public int Length
        {
            get { return _length; }
            set
            {
                _length = value;
                if (value <= 0) return;
                Width =
                    (int)((value + 1) * (Context != null ? Context.FieldCharWidth : 7)) +
                    (Context != null ? Context.FieldPaddings : 8);
            }
        }

        private int _labelLength;
        public int LabelLength
        {
            get { return _labelLength; }
            set
            {
                _labelLength = value;
                LabelWidth = value == 0 ? 0 :
                     (int)((value + 1) * (Context != null ? Context.FieldCharWidth : 7)) + 5;
            }
        }


        protected void LoadFromTerm(Term term)
        {
            Flex = term.Flex;
            Width = term.Width;
            Length = term.Length;
        }

        public void ApplyToComponent(AbstractComponent cmp)
        {
            cmp.AnchorHorizontal = Width.IsEmpty ? "0" : null;
            cmp.Flex = Width.IsEmpty ? Flex : 0;
            cmp.Width = Width;
        }

        public void ApplyToContainer(AbstractContainer cmp) { ApplyToComponent(cmp); }

        public void ApplyToField(Ext.Net.Field cmp)
        {
            ApplyToComponent(cmp);

            if (!HidingTitle)
            {
                cmp.FieldLabel = Title;
                if (LabelWidth > 0)
                    cmp.LabelWidth = LabelWidth;
            }
        }

        public void ApplyToField(Ext.Net.TextFieldBase cmp)
        {
            ApplyToField((Ext.Net.Field)cmp);
            var t = Title;
            if (t != null)
                cmp.EmptyText = HidingTitle ? t.Empty ?? t.Name : t.Empty;
        }

        public void ApplyToField(Ext.Net.TriggerFieldBase cmp)
        {
            ApplyToField((Ext.Net.TextFieldBase)cmp);
            if (Length > 0 && Context != null && !cmp.HideTrigger)
                cmp.Width = (int)cmp.Width.Value + Context.FieldTriggerWidth;
        }

        public void ApplyToField(Ext.Net.NumberFieldBase cmp)
        {
            if (Have(TermTags.Simple))
                cmp.HideTrigger = true;
            ApplyToField((Ext.Net.TriggerFieldBase)cmp);
        }

        public void ApplyToGrid(GridPanel cmp)
        {
            ApplyToComponent(cmp);
        }


        public void ApplyListeners(Ext.Net.FieldListeners listeners)
        {
            if (Term == null) return;

            if (Term.ChangeScript != null)
            {
                var s = Term.ChangeScript.ToScript(this);
                listeners.Change.Fn = s;
            }
        }

        #endregion


        public override string ToString()
        {
            return "TermNode " + (Term != null ? Term.ToString().TrimStart(false, "Term ") : null) + " - " + (Editor != null ? Editor.ToString() : null);
        }

        public string GetPathName()
        {
            var path = Parent != null ? Parent.GetPathName() : null;
            path += Term != null ? Term.Name : null;
            if (path.yes() && path[path.Length - 1] != '\\') path = path + '\\';
            return path;
        }



        #region Items

        protected List<TermNode> Items = new List<TermNode>();

        public TermNode this[int index] { get { return Items[index]; } }
        public int Count { get { return Items.Count; } }

        public TermNode Add(TermNode node)
        {
            if (Editor != null) Editor.BeforeAddTermNodeItem(this, ref node);
            if (node == null) return null;
            Items.Add(node);
            node.Parent = this;
            return node;
        }

        public IEnumerator<TermNode> GetEnumerator() { return new Enumerator(this); }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new Enumerator(this); }

        public class Enumerator : IEnumerator<TermNode>
        {
            public Enumerator(TermNode node) { Node = node; }

            public readonly TermNode Node;
            public int Index = -1;

            public TermNode Current { get { return Node[Index]; } }
            public void Dispose() { }
            object System.Collections.IEnumerator.Current { get { return Node[Index]; } }
            public bool MoveNext() { return ++Index < Node.Count; }
            public void Reset() { Index = 0; }
        }

        #endregion


        #region NewControls

        public IEnumerable<AbstractComponent> NewControls(UisCreateControlArgs e)
        {
            var list = Editor != null
                ? Editor.NewControls(this, e)
                : NewItemControls(e);
            return list;
        }

        public IEnumerable<AbstractComponent> NewItemControls(UisCreateControlArgs e)
        {
            var list = Items.SelectMany(item => item.NewControls(e));
            return list;
        }

        #endregion


        #region Tags

        public void Add(TermTag tag)
        {
            throw new NotImplementedException();
            //if (Term != null) Term.Add(tag);
        }

        public bool Have(TermTag tag) { return Term != null && Term.Tags.Contains(tag); }
        public bool ParentHave(TermTag tag) { return Parent != null && Parent.Have(tag); }

        #endregion

        /// <summary>
        /// Показывает, что его можно использовать в UI на форме для Ext.form.Term
        /// </summary>
        /// <returns></returns>
        public bool HasFieldName() { return Term != null && Term.Items.Count == 0 && Term.Name.yes(); }

        public TermTitle GetTitle(TermNode parentNode = null)
        {
            return Term != null ? Term.GetTitle(parentNode ?? Parent) : null;
        }

        public bool HidingTitle { get; set; }


        #region Find

        public TermNode FindItem(Func<TermNode, bool> match)
        {
            foreach (var item in Items)
            {
                if (match(item)) return item;
            }

            return null;
        }

        public TermNode FindSibling(Func<TermNode, bool> match)
        {
            return Parent != null ? Parent.FindItem(match) : null;
        }

        public TermNode FindUp(Func<TermNode, bool> match)
        {
            var node = FindSibling(match);
            if (node == null && Parent != null)
                node = Parent.FindUp(match);
            return node;
        }

        public TermNode FindDown(Func<TermNode, bool> match)
        {
            var node = FindItem(match);
            if (node != null) return node;

            foreach (var item in Items)
            {
                node = item.FindDown(match);
                if (node != null) break;
            }

            return node;
        }

        public TermNode FindNearest(Func<TermNode, bool> match)
        {
            if (match(this)) return this;

            var node = FindDown(match);
            if (node != null) return node;

            var p0 = this;
            var p = Parent;
            while (p != null)
            {
                foreach (var item in p.Items)
                {
                    if (item == p0) continue;
                    node = item.FindDown(match);
                    if (node != null) return node;
                }
                p0 = p;
                p = p.Parent;
            }

            node = FindSibling(match);
            if (node == null && Parent != null)
                node = Parent.FindUp(match);
            return node;
        }



        public TermNode FindItem(Func<TermNode, int> match)
        {
            if (match == null)
                throw new ArgumentNullException("match");

            foreach (var item in Items)
            {
                var result = match(item);
                if (result == 1) return item;
                if (result < 0) return null;
            }

            return null;
        }

        public TermNode FindSibling(Func<TermNode, int> match)
        {
            return Parent != null ? Parent.FindItem(match) : null;
        }

        public TermNode FindUp(Func<TermNode, int> match)
        {
            var node = FindSibling(match);
            if (node == null && Parent != null)
                node = Parent.FindUp(match);
            return node;
        }

        public TermNode FindDown(Func<TermNode, int> match)
        {
            var node = FindItem(match);
            if (node == null)
            {
                foreach (var item in Items)
                {
                    node = item.FindDown(match);
                    if (node != null) break;
                }
            }

            return node;
        }


        #endregion


        #region Lookup

        public TermNode LookupEntityNode
        {
            get
            {
                if (_LookupEntityNode == null && Term != null)
                {
                    var lentity = Term.LookupEntity;
                    _LookupEntityNode = lentity == null ? null : FindUp(a => a.Term == lentity);
                }
                return _LookupEntityNode;
            }
            set
            {
                _LookupEntityNode = value;
            }
        } TermNode _LookupEntityNode;

        #endregion

        public string GetInitScript()
        {
            var sb = new StringBuilder("\r\n\r\nvar uisRootPageTermNode = ");
            GetInitScript(sb);
            sb.AppendLine(";\r\n");
            return sb.ToString();
        }

        protected void GetInitScript(StringBuilder sb)
        {
            sb.AppendLine("new Ext.uis.TermNode({");
            if (Term != null && Term.FieldName.no() && Term.Name.yes()) 
                sb.AppendLine("// " + Term.Name);
            sb.AppendLine("    id: '" + ID + "'");
            if (Term != null && Term.FieldName.yes())
                sb.AppendLine("  , name: '" + Term.FieldName + "'");
            if (StoreIsNative)
                sb.AppendLine("  , store: '" + Store.ID + "'");
            if (Items.Count > 0)
            {
                sb.AppendLine("  , items: [");
                var isnext = false;
                foreach (var item in Items)
                {
                    if (isnext) sb.AppendLine(","); else isnext = true;
                    item.GetInitScript(sb);
                }
                sb.AppendLine("\r\n    ]");
            }
            sb.Append("})");
        }

    }


}



